let socket;
/**
 * serverName, port, _ctx, uname为后端提供的参数
 * @type {string}
 */
// let socketUrl = "ws://127.0.0.1:8899/netty/websocket/";
let socketUrl = "ws://127.0.0.1:8080/websocket/";
/**
 * 重新连接锁
 * 防止重复调用
 * @type {boolean}
 */
let lockReconnect = false;
/**
 * 不支持websocket的提示 只在第一次加载页面时展示
 * @type {boolean}
 */
let first = true;
/**
 * 服务端对心跳检测的响应值
 */
let v;
/**
 * 处理服务端消息推送的 function
 */
let onmessageFunc;
/**
 * 尝试重新连接的定时器
 */
let retryer;
/**
 * 重新连接次数
 * @type {number}
 */
let countLimit = 10;

let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
let randomStr = function (len) {
    len = len || 32;
    let maxPos = chars.length;
    let pwd = [];
    for (let i = 0; i < len; i++) {
        pwd.push(chars.charAt(Math.floor(Math.random() * maxPos)));
    }
    return pwd.join('');
}
/**
 * websocket构建对象
 * @param onmessageCall
 * @param unionName
 * @constructor
 */
let SocketBuilder = function (onmessageCall, unionName) {
    onmessageFunc = onmessageCall;
    if (unionName) {
        SocketBuilder.unionName = unionName;
    } else {
        SocketBuilder.unionName = 'guest';
    }
    this.init();
};

/**
 * 初始化websocket对象
 */
SocketBuilder.prototype.init = function () {
    console.info('初始化 websocket 对象......');
    this.createWebsocket();
    // 当前连接次数
    let count = 0;
    let temp = setInterval(() => {
        if (count > countLimit) {
            clearInterval(temp);
            layer.alert("WebSocket连接出现了问题<br>可能暂时无法使用即时通讯功能", {icon: 0});
        }
        if (socket && (socket.readyState === WebSocket.OPEN)) {
            this.initSocketEvent();
            clearInterval(temp);
        }
        count++;
    }, 500);
    retryer = temp;
}

/* 心跳检测,每5s心跳一次 start */
/**
 * 重新连接次数上限
 * @type {number}
 */
SocketBuilder.prototype.reconnectLimit = 50;
/**
 * 心跳检测时间间隔
 * @type {number}
 */
SocketBuilder.prototype.interval = 2000; // 心跳间隔(ms)
/**
 * 延迟验证的时间
 * socket对象开始被初始化的时候会经过几个状态可能会有一些延迟
 * 通过等待一段时间去验证socket对象当前的状态
 * 0 (WebSocket.CONNECTING), 正在链接中
 * 1 (WebSocket.OPEN), 已经链接并且可以通讯
 * 2 (WebSocket.CLOSING), 连接正在关闭
 * 3 (WebSocket.CLOSED), 连接已关闭或者没有链接成功
 * @see 延迟验证时间(该值一定要小于 interval)
 * @type {number}
 */
SocketBuilder.prototype.intervalDelay = 500;
/**
 * 心跳检测的 setInterval的值
 * @type {number}
 */
SocketBuilder.prototype.intervalIndex = -1;
/**
 * 启动心跳检测
 */
SocketBuilder.prototype.start = function () {
    this.intervalIndex = setInterval(() => {
        // 这里发送一个心跳，后端收到后，返回一个心跳消息
        // onmessage拿到返回的心跳就说明连接正常
        if (socket) {
            let ranObj = {msg: 'ok' + randomStr(4)};
            socket.send(JSON.stringify(ranObj));
            // 延迟 #{intervalDelay} 毫秒后比对服务端返回的消息
            const signName = '当前用户标识: ' + SocketBuilder.unionName;
            setTimeout(() => {
                if (v && (v.msg !== ranObj.msg)) {
                    console.info("与服务器连接出现问题");
                    this.stop();
                    this.reconnect();
                    return;
                }
                if (v && (v.isOnline === false)) {
                    layer.alert("由于长时间未操作<br>当前您已退出登录状态<br>请重新登录", {icon: 0});
                    this.stop();
                    return;
                }
                document.getElementById("heart-sign").innerHTML = '<i class="layui-icon layui-icon-heart-fill layui-anim layui-anim-scalesmall-spring" style="font-size: 24px;color: red;"></i> ' + signName;
            }, this.intervalDelay);
        }
    }, this.interval);
    // 心跳检测
    console.info("启动 Websocket 心跳检测");
    return SocketBuilder;
}
/**
 * 当监听到连接断开或其他无法连接的问题时调用
 * 用于停止心跳检测, 以便于下一步尝试重新连接
 */
SocketBuilder.prototype.stop = function () {
    clearInterval(this.intervalIndex);
    return SocketBuilder;
}
/**
 * 服务器端返回心跳响应后保存该响应
 * @param contentData
 */
SocketBuilder.prototype.callBackValue = function (contentData) {
    v = contentData;
}
/**
 * 重新连接
 */
SocketBuilder.prototype.reconnect = function () {
    // 当前正在进行重新连接的直接返回
    if (lockReconnect) {
        return;
    }
    lockReconnect = true;
    // 已重新连接的次数
    let count = 1;
    // 没连接上会尝试重连，设置延迟避免请求过多
    let temp = setInterval(() => {
        // 超过连接次数, 放弃
        if (count > this.reconnectLimit) {
            clearInterval(temp);
            layer.alert("WebSocket重新连接失败<br>请刷新页面或联系管理员", {icon: 0});
        }
        console.info("尝试重连...第" + (count++) + "次");
        // 创建websocket对象
        this.createWebsocket();
        // 延迟判断连接状态
        setTimeout(() => {
            // 重新连接成功
            if (socket.readyState === WebSocket.OPEN) {
                lockReconnect = false;
                clearInterval(temp);
                // 重新初始化事件
                this.initSocketEvent()
                console.info("WebSocket 重新连接成功");
            }
        }, this.intervalDelay);
    }, this.interval);
    retryer = temp;
}
/* 心跳检测,每5s心跳一次 end */

/**
 * 创建websocket对象
 * @param serverName
 * @param port
 * @param uname
 */
SocketBuilder.prototype.createWebsocket = () => {
    if (typeof (WebSocket) == "undefined" && first) {
        layer.alert(
            "您的浏览器不支持WebSocket<br>" +
            "国产浏览器请使用极速模式<br>" +
            "或者使用Chrome, FireFox等浏览器",
            {icon: 0}
        );
        return false;
    }
    socket = new WebSocket(socketUrl + (SocketBuilder.unionName || ''));
    /**
     * 这里单独设置一下监听onerror事件
     * 主要针对服务器宕机之后的重启情况
     * @param event
     */
    socket.onerror = (event) => {
        console.warn("onerror", event);
        layer.alert("WebSocket发生了错误<br>尝试重新登录可能解决该问题", {icon: 0});
        clearInterval(retryer);
    }
    first = false;
}

/**
 * 声明事件
 */
SocketBuilder.prototype.initSocketEvent = function () {
    /**
     * websocket通道连接事件
     * 与服务器正常连接后调用
     */
    socket.onopen = () => {
        console.log("onopen");
    };
    /**
     * 获得消息事件
     * 服务器主动推送消息后调用
     */
    if (onmessageFunc && typeof onmessageFunc === 'function') {
        socket.onmessage = onmessageFunc;
    } else {
        socket.onmessage = (msg) => {
            console.info("onmessage", msg);
        };
    }
    /**
     * 关闭事件
     * 与服务器断开连接后调用
     */
    socket.onclose = () => {
        this.stop();
        setTimeout(() => {
            document.getElementById("heart-sign").innerHTML = '';
        }, this.interval)
        console.info("onclose");
    };
    /**
     * 由于报错抛出异常后调用
     * @param event
     */
    socket.onerror = (event) => {
        console.warn("onerror", event);
        layer.alert("WebSocket发生了错误<br>尝试重新登录可能解决该问题", {icon: 0});
    }
    this.start();
}

